/*
 * Ingenic IMP AO implement.

 * Copyright (C) 2017 Ingenic Semiconductor Co.,Ltd

 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <linux/soundcard.h>
#define __USE_GNU
#include <fcntl.h>

#include <imp/imp_audio.h>
#include <imp/imp_log.h>
#include <imp/imp_system.h>
#include <system/system.h>
#include <dsystem/funcs.h>

#include "audio_common.h"
#include "abuf.h"
#include "audioProcess/aec/profiles/profiles_management.h"

#include "audio_process.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "ao"

extern char pathbuff[128];

static char profile_buff[101] = "";

#define AO_SOURCE_RECORD_FILE "ao_source_record.pcm"
#define AO_TARGET_RECORD_FILE "ao_target_record.pcm"
FILE *ao_source_file = NULL;
FILE *ao_target_file = NULL;
volatile int ao_frm_num = 0;

#define DSP_DEVICE  "/dev/dsp"

static AudioOutDev aoDev[MAX_AO_DEV+1];

static pthread_mutex_t mutex_dev = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_dev = PTHREAD_COND_INITIALIZER;
static pthread_cond_t cond_ao_send = PTHREAD_COND_INITIALIZER;

extern void *g_lib_audioProcess_agc_handle;
extern void *g_lib_audioProcess_hpf_handle;
extern audio_dev_info_t g_audio_dev_info;
/*agc function pointer*/
static pFun_agc_create fun_ao_agc_create = NULL;
static pFun_agc_set_config fun_ao_agc_set_config = NULL;
static pFun_agc_get_config fun_ao_agc_get_config = NULL;
static pFun_agc_process fun_ao_agc_process = NULL;
static pFun_agc_free fun_ao_agc_free = NULL;
/*hpf function pointer*/
static pFun_hpf_create fun_ao_hpf_create = NULL;
static pFun_hpf_process fun_ao_hpf_process = NULL;
static pFun_hpf_free fun_ao_hpf_free = NULL;
FilterState handle_ao_hpf;
extern const int16_t kFilterCoefficients8kHz[5];
extern const int16_t kFilterCoefficients[5];
int16_t AoCoefficients[5] = {0};
int ao_agc_mode = 0;
int frame_cnt = 0;
static int __ao_dev_write(int aodev, void *buf, int count)
{
	int ret = -1;
	struct audio_ouput_stream ao_stream;
	AudioOutDev *paodev = &aoDev[aodev];
	ao_stream.data = buf;
	ao_stream.size = count;
	//printf("### buf = %p, count = %d\n", buf, count);
	ret = ioctl(paodev->aofd, AMIC_AO_SET_STREAM, &ao_stream);
	if (ret != 0) {
		printf("err: %s\n", strerror(errno));
	}
	return ao_stream.size;
}

int __ao_dev_set_gain(int audioDevId, int aoChn, int *aogain)
{
	int ret = -1;
	AudioOutDev *pdev = &aoDev[audioDevId];
	struct volume gainctl;
	gainctl.channel = 1;
	gainctl.gain = *aogain;
	//gainctl.gain[1] = *aogain;
	ret = ioctl(pdev->aofd, AMIC_SPK_SET_GAIN, &gainctl);
	if (0 != ret) {
		printf("err: %s\n", strerror(errno));
	}
	return ret;
}

int __ao_dev_set_dgain(int audioDevId, int aoChn, int *dGain)
{
	int ret = -1;
	AudioOutDev *pdev = &aoDev[audioDevId];
	struct volume gainctl;
	gainctl.channel = 1;
	gainctl.gain = *dGain;
	//gainctl.gain[1] = *dGain;
	ret = ioctl(pdev->aofd, AMIC_SPK_SET_VOLUME, &gainctl);
	if (0 != ret) {
		printf("err: %s\n", strerror(errno));
	}
	return ret;
}

static void *_ao_play_thread(void *argv);
int __ao_dev_init(int audioDevId)
{
	int ret = -1;
	AudioOutDev *pdev = &aoDev[audioDevId];
	struct audio_parameter AoPara;
	pdev->devID = audioDevId;
	pdev->aofd = open(DSP_DEVICE, O_WRONLY | O_CLOEXEC);
	if (pdev->aofd < 0) {
		IMP_LOG_ERR(TAG, "open %s error\n", DSP_DEVICE);
		return -1;
	}

	AoPara.rate = pdev->audioOAttr.samplerate;
	AoPara.channel = pdev->audioOAttr.soundmode;
	AoPara.format = 16;
    ret = ioctl(pdev->aofd, AMIC_AO_SET_PARAM, &AoPara);
	if (ret) {
		IMP_LOG_ERR(TAG, "Ao Device AMIC_AO_SET_PARAM error.\n");
		return -1;
	}
	ret = ioctl(pdev->aofd, AMIC_AO_ENABLE_STREAM, 1);
	if (ret) {
		IMP_LOG_ERR(TAG, "Ao Device set AMIC_AO_ENABLE_STREAM error.\n");
		return -1;
	}

	if (pthread_create(&pdev->aoProcess, NULL, _ao_play_thread, pdev) != 0) {
		IMP_LOG_ERR(TAG, "start audio device thread error\n");
		return -1;
	}
	return ret;
}

static void _ao_thread_post();
int __ao_dev_deinit(int audioDevId)
{
	int ret = 0;
	AudioOutDev *pdev = &aoDev[audioDevId];
	pdev->aoEnable = 0;
	_ao_thread_post();
	pthread_join(pdev->aoProcess, NULL);
	ret = ioctl(pdev->aofd,AMIC_AO_DISABLE_STREAM, 1);
	if (ret) {
		IMP_LOG_ERR(TAG, "Ao Device set AMIC_AO_DISABLE_STREAM error.\n");
	}
	if (pdev->aofd > 0) {
		close(pdev->aofd);
		pdev->aofd = -1;
	}

	return 0;
}

static int __ao_dev_clearbuf(int audioDevId, int aoChn)
{
	int ret = -1;
	AudioOutDev *pdev = &aoDev[audioDevId];

	ret = ioctl(pdev->aofd,AMIC_AO_CLEAR_STREAM,NULL);
	if (ret != 0) {
		 IMP_LOG_ERR(TAG, "Ao Device set SNDCTL_EXT_FLUSH_STREAM error.\n");
		 printf("err: %s\n", strerror(errno));
		 return -1;
	}

	return 0;
}

static int __ao_dev_flushchnbuf(int audioDevId, int aoChn)
{
	int ret = -1;
	AudioOutDev *pdev = &aoDev[audioDevId];

	ret = ioctl(pdev->aofd, AMIC_AO_SYNC_STREAM, NULL);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Ao Device set AMIC_AO_FLUSH_STREAM error.\n");
		printf("err: %s\n", strerror(errno));
		return -1;
	}

	return 0;
}

static inline int _ao_agc_lock(AudioOutChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_lock(&pch->mutex_ao_agc);
}

static inline int _ao_agc_unlock(AudioOutChn *pch)
{
	if(NULL == pch)
		return -1;

	return pthread_mutex_unlock(&pch->mutex_ao_agc);
}

static inline int _ao_hpf_lock(AudioOutChn *pch)
{
	if (NULL == pch)
		return -1;
	return pthread_mutex_lock(&pch->mutex_ao_hpf);
}

static inline int _ao_hpf_unlock(AudioOutChn *pch)
{
	if (NULL == pch)
		return -1;

	return pthread_mutex_unlock(&pch->mutex_ao_hpf);
}

static int _ao_init_audioProcess_mutex(AudioOutChn *pch)
{
	if (NULL == pch) {
		IMP_LOG_ERR(TAG,"fun:%s AudioOutChn pch is empty\n",__func__);
		return -1;
	}
	int ret = 0;

	if (pthread_mutex_init(&pch->mutex_ao_agc,NULL) != 0)
		ret = -1;
	if (pthread_mutex_init(&pch->mutex_ao_hpf,NULL) != 0)
		ret = -1;

	return ret;

}

static int  _ao_deinit_audioProcess_mutex(AudioOutChn *pch)
{
	if (NULL == pch) {
		IMP_LOG_ERR(TAG,"fun:%s,AudioOutChn pch is empty.\n",__func__);
		return -1;
	}

	int ret = 0;

	if (pthread_mutex_destroy(&pch->mutex_ao_agc) != 0)
		ret = -1;
	if (pthread_mutex_destroy(&pch->mutex_ao_hpf) != 0)
		ret = -1;

	return ret;

}

static void _ao_send_wait()
{
	struct timeval now;
	struct timespec timeout;

	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	if (pthread_cond_timedwait(&cond_ao_send, &mutex_dev, &timeout) == ETIMEDOUT)
		IMP_LOG_WARN(TAG, "Audio Out Send Wait timeout\n");
}
static void _ao_sync_wait(AudioOutChn *pch)
{
	if(NULL == pch) {
		IMP_LOG_ERR(TAG, "fun:%s, line:%d,AudioOutchn is empty",__func__,__LINE__);
		return;
	}
	struct timeval now;
	struct timespec timeout;

	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	if (pthread_cond_timedwait(&pch->cond_flush, &pch->mutex_flush, &timeout) == ETIMEDOUT)
		IMP_LOG_WARN(TAG, "_ao_sync_wait timeout\n");
}

static void _ao_send_post()
{
	pthread_cond_signal(&cond_ao_send);
}

static void _ao_thread_wait()
{
	struct timeval now;
	struct timespec timeout;

	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	if (pthread_cond_timedwait(&cond_dev, &mutex_dev, &timeout) == ETIMEDOUT) {
		//IMP_LOG_WARN(TAG, "ao thread wait timeout\n");
		//printf("ao thread wait timeout\n");
		;
	}
}

static void _ao_thread_post()
{
	pthread_cond_signal(&cond_dev);
}

static inline void _ao_chn_lock(AudioOutChn *pch)
{
	pthread_mutex_lock(&pch->mutex_chn);
}

static inline void _ao_chn_unlock(AudioOutChn *pch)
{
	pthread_mutex_unlock(&pch->mutex_chn);
}

struct aobufinfo {
	int len;
	void *buf;
};

int _ao_chn_enable(int audioDevId, int aoChn)
{
	int ret = -1;
	AudioOutChn *pch = NULL;
	AudioOutDev *pdev = NULL;
	pdev = &aoDev[audioDevId];
	pch = &aoDev[audioDevId].audioOutChn[aoChn];

	pthread_mutex_init(&pch->mutex_chn, NULL);
	pthread_mutex_init(&pch->mutex_flush, NULL);
	pthread_cond_init(&pch->cond_flush, NULL);

	ret = pthread_mutex_lock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	pch->ao_buf = audio_buf_alloc(pdev->audioOAttr.frmNum, pdev->max_buf_size, sizeof(struct aobufinfo));

	if (NULL == pch->ao_buf) {
		IMP_LOG_ERR(TAG, "ao_buf alloc error\n");
		_ao_chn_unlock(pch);
		return -1;
	}

	pch->status.chnTotalNum = pdev->audioOAttr.frmNum;
	pch->status.chnFreeNum = pdev->audioOAttr.frmNum;
	pch->status.chnBusyNum = 0;
	/*Set default ao vol.*/
	pch->ao_ch_keep_old_vol_coef = pow(10, ((60.0 * 0.5) - 30.0) / 20.0);
	pch->vol.vol = pch->ao_ch_keep_old_vol_coef;
	pch->ao_ch_keep_old_vol = 60;
	pch->enable = 1;
	pch->pause_done = 0;
	pch->sync = 1;
	pch->cache_en = 1;
	pch->disable_done = 0;
	ret = pthread_mutex_unlock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	return 0;
}

int _ao_chn_disable(int audioDevId, int aoChn)
{
	int ret = -1;
	int retry = 0;
	AudioOutChn *pch = NULL;
	pch = &aoDev[audioDevId].audioOutChn[aoChn];

	ret = pthread_mutex_lock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	pch->enable = 0;
	_ao_thread_post();
	ret = pthread_mutex_unlock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	while ((retry < 20) && (1 != pch->disable_done)) {
		usleep(100*1000);
		retry++;
	}
	if (retry >= 30) {
		printf("warn: wait busy buf too long time\n");
	}
	if (NULL != pch->ao_buf)
		audio_buf_free(pch->ao_buf);
	pch->disable_done = 0;

	pthread_mutex_destroy(&pch->mutex_flush);
	pthread_cond_destroy(&pch->cond_flush);

	return 0;
}

static void *_ao_play_mute_thread(void *argv)
{
	int i = 0;
	AudioOutChn *pch = argv;
	double * vol = &pch->vol.vol;

	char thread_name[64];
	sprintf(thread_name, "ao-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	for (i = pch->ao_ch_keep_old_vol; i > -30; i -= 3) {
		*vol = pow(10, ((((double)i) * 0.5) - 30.0) / 20.0);
		usleep(1000*150);
	}
	*vol = 0;

	return NULL;
}

static void *_ao_play_unmute_thread(void *argv)
{
	int i = 0;

	char thread_name[64];
	sprintf(thread_name, "ao-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);
	AudioOutChn *pch = argv;

	double * vol = &pch->vol.vol;
	for (i = -30; i < pch->ao_ch_keep_old_vol; i += 3) {
		*vol = pow(10, ((((double)i) * 0.5) - 30.0) / 20.0);
		usleep(1000*150);
	}
	*vol = pow(10, ((((double)pch->ao_ch_keep_old_vol) * 0.5) - 30.0) / 20.0);

	return NULL;
}

static int _ao_play(int aodev, void *buf, int len)
{
	int written = 0;
	int count = len;
	char *tmp_buf = buf;
	while(count > 3) {
		written = __ao_dev_write(aodev, tmp_buf , count);
		if (written < 0) {
			IMP_LOG_ERR(TAG, "write aofd error\n");
			return -1;
		}
		count -= written;
		tmp_buf += written;
	}
	return len - count;
}

static void *_ao_play_thread(void *argv)
{
	int ret = -1;
	int devID = 0;
	AudioOutDev *pdev = argv;
	AudioOutChn *pch = NULL;
	char thread_name[64];
	void *aobuf = NULL;
	struct aobufinfo *info = NULL;

	sprintf(thread_name, "ao-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);
	devID = pdev->devID;

	pch = &aoDev[devID].audioOutChn[0];
	while (1) {
		int wait = 0;
		ret = pthread_mutex_lock(&mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
		do {
			wait = 0;
			aobuf = NULL;
			int dev_en = pdev->aoEnable;
			int ch_en = pch->enable;
			int ch_p = pch->pause;
			if ((1 == dev_en) && (0 == ch_en)) {
				if (0 == ch_en) {
					pch->disable_done = 1;
				}
				//post ch disable
				wait = 1;
			} else if ((1 == dev_en) && (1 == ch_en)) {
				if(!pch->cache_en) pch->sync = 0;
				if (1 == ch_p) {
					pch->pause_done = 1;
					//post pause complete
					wait = 1;
				} else {
					if ((1 == pch->sync) && (pch->status.chnBusyNum < pch->status.chnFreeNum)) {
							wait = 1;
					} else {
						aobuf = audio_buf_get_node(pch->ao_buf, AUDIO_BUF_FULL);
						if (NULL == aobuf) {
							pch->sync = 1;
							wait = 1;
						} else {
							//printf("##### aobuf = %p\n", aobuf);
							if (1 == pch->sync)
								pch->sync = 0;
						}
					}
				}
			}
			if (1 == wait) {
				_ao_thread_wait();
			}
		} while (1 == wait);
		//printf("!!!!!!!!!!!!!!!!!! aobuf = %p \n", aobuf);
		if (0 == pdev->aoEnable) {
			ret = pthread_mutex_unlock(&mutex_dev);
			if (0 != ret) {
				printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
				break;
			}
			break;
		}
		ret = pthread_mutex_unlock(&mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
		if (NULL == aobuf) {
			printf("err: %s,%d aobuf == NULL\n", __func__, __LINE__);
			continue;
		}
		info = audio_buf_node_get_info(aobuf);
		_ao_play(devID, info->buf, info->len);
		ret = pthread_mutex_lock(&mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
		audio_buf_put_node(pch->ao_buf, aobuf, AUDIO_BUF_EMPTY);

		pthread_mutex_lock(&pch->mutex_flush);
		pch->status.chnBusyNum--;
		pch->status.chnFreeNum++;
		if(0 == pch->status.chnBusyNum)
			pthread_cond_signal(&pch->cond_flush);
		pthread_mutex_unlock(&pch->mutex_flush);

#if 0
		printf("status:busy = %d empty = %d, buf:busy = %d empty = %d\n",
				pch->status.chnBusyNum, pch->status.chnFreeNum,
				audio_buf_get_num(pch->ao_buf, AUDIO_BUF_FULL), audio_buf_get_num(pch->ao_buf, AUDIO_BUF_EMPTY));
#endif
		_ao_send_post();
		ret = pthread_mutex_unlock(&mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
	}

	IMP_LOG_INFO(TAG, "ao record thread exit\n");
	pthread_exit(0);
}

int _ao_get_buf_size(void)
{
	AudioOutDev *pdev = NULL;
	int size = 0;
	pdev = &aoDev[MAX_AO_DEV];
	size = pdev->audioOAttr.numPerFrm*sizeof(short);
	return size;
}



int _ao_InitializeFilter(void * filter, int sample_rate_hz,int cofrequency)
{
	FilterState* hpf = (FilterState *)filter;
	if (hpf == NULL) {
		printf("hpf is NULL\n");
		return -1;
	}

	if(cofrequency != 0){
		Hpf_gen_filter_coefficients(AoCoefficients, sample_rate_hz, cofrequency);
		hpf->ba = AoCoefficients;
	}else{
		if (sample_rate_hz == kSampleRate8kHz) {
			hpf->ba = kFilterCoefficients8kHz;
		} else {
			hpf->ba = kFilterCoefficients;
		}
	}
	fun_ao_hpf_create(hpf->x,hpf->y,0,0,2,4);

	return 0;
}

int _ao_HPF_Filter(void * filter, short* data, int size)
{
	int ret = -1;
	char *p = (char *)data;
	int i = 0, k = 0, samples = 0;
	k = size / 160;
	samples = size / 2 / k;
	/*printf("%s: size = %d, samples = %d, k = %d\n", __func__, size, samples, k);*/

	for (i = 0; i < k; i++) {
		ret = fun_ao_hpf_process(filter,(short*)(p+160*i),samples);
		if (ret != 0) {
			printf("%s: Filter error\n", __func__);
			return -1;
		}
	}
	return 0;
}

int _ao_Agc_Process(void *agcInst, const int16_t *in_near, int16_t samples,
                      int16_t *out, int32_t inMicLevel,
                      int32_t *outMicLevel, int16_t echo,
                      uint8_t *saturationWarning, int size)
{
	int ret = -1;
	int k, i = size / (samples*2);
	/* printf("size = %d, samples = %d, i = %d\n", size, samples, i); */
	const int16_t *p = in_near;
	int16_t *q = out;

	for(k = 0; k < i; k++) {
		const int16_t *ptmp = p + samples*k;
		int16_t *qtmp = q + samples*k;
		const int16_t *const*pp = &ptmp;
		int16_t *const*qq = &qtmp;
		ret = fun_ao_agc_process(agcInst,pp,1,samples,qq,inMicLevel,outMicLevel,echo,saturationWarning);
		if (ret != 0) {
			printf("%s, line %d : agc proccess error\n", __func__, __LINE__);
			return -1;
		}
	}
	return 0;
}

static int _set_aoAgcEnableFlag(int audioDevId,int aoChn)
{
	AudioOutChn *pch = NULL;

	if (audioDevId > MAX_AO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aoChn > MAX_AO_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pch->ao_agc_enable_suppression = 1;

	return 0;
}

static int _reset_aoAgcEnableFlag(int audioDevId,int aoChn)
{
	 AudioOutChn *pch = NULL;

	if (audioDevId > MAX_AO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (aoChn > MAX_AO_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pch->ao_agc_enable_suppression = 0;

	return 0;

}

static int _set_aoHpfEnableFlag(int audioDevId,int aoChn)
{
	AudioOutChn *pch = NULL;

	if (audioDevId > MAX_AO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aoChn > MAX_AO_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pch->ao_hpf_enable_suppression = 1;

	return 0;
}

static int _set_aoHpfCutoffFrequency(int audioDevId,int aoChn,int cofrequency)
{
	AudioOutChn *pch = NULL;

	if (audioDevId > MAX_AO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__,__LINE__);
		return 0;
	}

	if (aoChn > MAX_AO_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pch->hpf_co_frequency = cofrequency;

	return 0;
}

static int _get_aoHpfCutoffFrequency(int audioDevId,int aoChn)
{
	AudioOutChn *pch = &aoDev[audioDevId].audioOutChn[aoChn];
	return pch->hpf_co_frequency;
}

static int _reset_aoHpfEnableFlag(int audioDevId,int aoChn)
{
	AudioOutChn *pch = NULL;

	if (audioDevId > MAX_AO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (aoChn > MAX_AO_CHN) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pch->ao_hpf_enable_suppression = 0;

	return 0;

}

static int _ao_check_user_attr(AudioOutDev *pdev, IMPAudioIOAttr *pattr)
{
	if (NULL == pdev||NULL == pattr) {
		IMP_LOG_ERR(TAG,"fun:%s,parameter is invalid.\n",__func__);
		return -1;
	}

	pdev->user_numperfrm = pattr->numPerFrm;

	if (pattr->numPerFrm < (pattr->samplerate/100)*4) /*smaller than 40ms*/
		pattr->numPerFrm = (pattr->samplerate/100)*4;

	return 0;
}

static void * _ao_alloc_buf(int size)
{
	void *pBuf = NULL;
	if (size <= 0) {
		IMP_LOG_ERR(TAG,"fun:%s,parameter size is smaller than zero.\n",__func__);
		return NULL;
	}
	pBuf = malloc(size);
	if (NULL == pBuf) {
		IMP_LOG_ERR(TAG,"fun:%s,malloc buf failed\n",__func__);
		return NULL;
	}
	memset(pBuf, 0x0, size);

	return pBuf;
}

static void _ao_free_buf(void *pBuf)
{
	if (pBuf) {
		free(pBuf);
	}
}

int impdbg_ao_dev_info(void *buf, int msize, void **arg)
{
	AudioOutDev *pdev = NULL;
	dformat *df = buf;
	int len = 0;
	char *dbuf = NULL;
	dbuf = (char *)df->out.udf;

 	if(buf == NULL)
		return -1;

	pdev = &aoDev[0];
	len = sprintf(dbuf, "\tDEV(ID:%d)\nAudioOutAttr:\n\tSamplerate:%5d\n\tbitwidth: %5d\n\tsoundmode: %5d\n\tfrmNum: %5d\n\tnumPerFrm:%5d\n\tchnCnt:%5d\n",
					0, pdev->audioOAttr.samplerate, pdev->audioOAttr.bitwidth, pdev->audioOAttr.soundmode, pdev->audioOAttr.frmNum, pdev->audioOAttr.numPerFrm, pdev->audioOAttr.chnCnt);

	len += sprintf(dbuf+len, "ChannalAttr:\n\tminVol:%5d\n\tmaxVol:%5d\n\tsetVol:%5d\n\tGain:%5d\n",
					pdev->audioOutChn[0].vol.minVol,pdev->audioOutChn[0].vol.maxVol,pdev->audioOutChn[0].ao_ch_keep_old_vol,pdev->audioOutChn[0].ao_ch_keep_old_gain);

	df->flg |= (1<<8);
	df->out.ret = 0;
	df->out.type = 0;
	df->out.size = len;

	return 0;
}

int impdbg_ao_get_frm(void *buf, int msize, void **arg)
{
 	if(buf == NULL){
		return -1;
	}
	dformat *df = buf;
	remove(AO_SOURCE_RECORD_FILE);
	remove(AO_TARGET_RECORD_FILE);
	ao_source_file = fopen( AO_SOURCE_RECORD_FILE, "wb");
	if(ao_source_file == NULL) {
		printf("open source file fail\n");
		return -1;
	}
	ao_target_file = fopen( AO_TARGET_RECORD_FILE, "wb");
	if(ao_target_file == NULL) {
		printf("open target file fail\n");
		return -1;
	}
	ao_frm_num = *((int *)(df->in.udf));
	if(ao_frm_num <= 0){
		printf("input frm(%d) err,clear file!!!\n",ao_frm_num);
		fclose(ao_source_file);
		fclose(ao_target_file);
		ao_source_file = NULL;
		ao_target_file = NULL;
		ao_frm_num = 0;
		remove(AO_SOURCE_RECORD_FILE);
		remove(AO_TARGET_RECORD_FILE);
		return -1;
	}else{
		while(ao_frm_num);
		fclose(ao_source_file);
		fclose(ao_target_file);
		ao_source_file = NULL;
		ao_target_file = NULL;
	}

	return 0;
}

void dbg_ao_get_frm(uint32_t* date_ptr, int date_size, file_addr addr)
{
	if(ao_source_file == NULL || ao_target_file == NULL){
		printf("collect file no exit\n");
		return ;
	}else{
		if(date_size == 0){
			printf("cannot write 0 byte to fail\n");
			return ;
		}
	}

	if(addr == WRITE_SOURCE){
		fwrite(date_ptr, 1, date_size, ao_source_file);
	}else{
		fwrite(date_ptr, 1, date_size, ao_target_file);
	}
}

int IMP_AO_IMPDBG_Init(void)
{
     int ret = 0;

     ret = func_init();
     ret += dsys_func_share_mem_register(FUNC_MODULE_AO_DEV, DBG_Ao_INFO, "impdbg_ao_dev_info", impdbg_ao_dev_info);
     ret += dsys_func_share_mem_register(FUNC_MODULE_AO_DEV, DBG_Ao_GET_FRM, "impdbg_ao_get_frm", impdbg_ao_get_frm);

	 return ret;
}

int IMP_AO_IMPDBG_DeInit(void)
{
     int ret = 0;

     ret = func_deinit();
     ret += dsys_func_unregister(FUNC_MODULE_AO_DEV, DBG_Ao_INFO);
     ret += dsys_func_unregister(FUNC_MODULE_AO_DEV, DBG_Ao_GET_FRM);

	 return ret;
}

typedef void Handle_agc;
Handle_agc *handle_ao_agc = NULL;
uint32_t agc_sample_o = 0;
int IMP_AO_EnableAgc(IMPAudioIOAttr *attr, IMPAudioAgcConfig agcConfig)
{
	IMP_LOG_INFO(TAG,"AO Enable AGC\n");
	int ret = -1;

	AgcConfig config;
	config.targetLevelDbfs = agcConfig.TargetLevelDbfs;
	config.compressionGaindB = agcConfig.CompressionGaindB;
	config.limiterEnable = 1;
	IMP_LOG_INFO(TAG, "AO AGC ENABLE: targetLevelDbfs = %d, compressionGaindB = %d, limiterEnable = %d\n",
			config.targetLevelDbfs, config.compressionGaindB, config.limiterEnable);
	/* printf("samplerate = %d, bitwidth = %d, soundmode = %d, frmNum = %d, numPerFrm = %d, chnCnt = %d\n", */
	/* 		attr->samplerate, attr->bitwidth, attr->soundmode, attr->frmNum, attr->numPerFrm, attr->chnCnt); */

	/* char * version = (char *)malloc(sizeof(char) * 20); */
	/* if (version != NULL) { */
	/* 	ret = WebRtcAgc_Version(version, 20); */
	/* 	IMP_LOG_INFO(TAG, "AGC version is: %s\n", version); */
	/* 	free(version); */
	/* } */

	ret =  init_audioProcess_library(LIB_AUDIOPROCESS_AGC_FILE_NAME, &g_lib_audioProcess_agc_handle);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n",__func__);
		return -1;
	}

	FUN_TYPE agcfun[5] = {{FUN_AGC_CREATE_NAME,(void**)&fun_ao_agc_create},
						{FUN_AGC_SET_CONFIG_NAME,(void**)&fun_ao_agc_set_config},
						{FUN_AGC_GET_CONFIG_NAME,(void**)&fun_ao_agc_get_config},
						{FUN_AGC_PROCESS_NAME,(void**)&fun_ao_agc_process},
						{FUN_AGC_FREE_NAME,(void**)&fun_ao_agc_free}};

	ret = get_fun_address(g_lib_audioProcess_agc_handle,agcfun,5);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"line:%d get fun address failed\n",__LINE__);
		return -1;
	}

	handle_ao_agc = fun_ao_agc_create();
	if (NULL == handle_ao_agc) {
		IMP_LOG_ERR(TAG, "fun:%s,agc create error.\n",__func__);
		return -1;
	}
	if (8000 == attr->samplerate) {
		agc_sample_o = 80;
	} else if ((16000 == attr->samplerate)||(32000 == attr->samplerate)||(48000 == attr->samplerate)) {
		agc_sample_o = 160;
	} else {
		IMP_LOG_ERR(TAG, "WebRtcAgc do not supoort samplerate: %d\n", attr->samplerate);
		return -1;
	}

	ret = fun_ao_agc_set_config(handle_ao_agc,0,255,ao_agc_mode,attr->samplerate,config);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "fun:%s agc set config error\n",__func__);
		return -1;
	}

	ret = _set_aoAgcEnableFlag(0,0);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"_set_aoAgcEnableFlag failed\n");
		return -1;
	}

	return 0;
}

int IMP_AO_DisableAgc(void)
{
	IMP_LOG_INFO(TAG, "AO AGC DISABLE\n");

	if (handle_ao_agc == NULL) {
		IMP_LOG_WARN(TAG, "AO AGC NOT ENABLED\n");
		return 0;
	}

	_reset_aoAgcEnableFlag(0,0);

	fun_ao_agc_free(handle_ao_agc);
	handle_ao_agc = NULL;
	fun_ao_agc_create = NULL;
	fun_ao_agc_set_config = NULL;
	fun_ao_agc_get_config = NULL;
	fun_ao_agc_process = NULL;
	fun_ao_agc_free = NULL;

	return 0;
}

//static int enable_ao_hpf_suppression = 0;
int IMP_AO_EnableHpf(IMPAudioIOAttr *attr)
{
	IMP_LOG_INFO(TAG,"AO HPF ENABLE\n");
	int ret = -1;
	int cofrequency = _get_aoHpfCutoffFrequency(0,0);
	if(cofrequency < 0 || cofrequency > attr->samplerate){
		IMP_LOG_ERR(TAG,"HPF cut-off frequency is illegal.\n");
		return -1;
	}

	ret = init_audioProcess_library(LIB_AUDIOPROCESS_HPF_FILE_NAME, &g_lib_audioProcess_hpf_handle);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s,init audioProcess library failed\n");
		return -1;
	}

	FUN_TYPE hpffun[3] = {{FUN_HPF_CREATE_NAME,(void**)&fun_ao_hpf_create},
				{FUN_HPF_PROCESS_NAME,(void**)&fun_ao_hpf_process},
				{FUN_HPF_FREE_NAME,(void**)&fun_ao_hpf_free}};

	ret = get_fun_address(g_lib_audioProcess_hpf_handle,hpffun,3);
	if (ret != 0){
		 IMP_LOG_ERR(TAG,"line:%d get fun address failed\n",__LINE__);
		 return -1;
	}

	char * version = (char *)malloc(sizeof(char) * 32);
	if (version != NULL) {
		ret = Hpf_Version(version, 32);
		IMP_LOG_INFO(TAG, "HPF version is: %s\n", version);
		free(version);
	}
	ret = _ao_InitializeFilter(&handle_ao_hpf, attr->samplerate,cofrequency);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "HPF InitializeFilter error.\n");
		return -1;
	}

	ret = _set_aoHpfEnableFlag(0,0);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"_set_aoHpfEnableFlag failed\n");
		return -1;
	}
	return 0;
}

int IMP_AO_SetHpfCoFrequency(int cofrequency)
{
	int ret = -1;
	ret = _set_aoHpfCutoffFrequency(0,0,cofrequency);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"_set_aoHpfCutoffFrequency failed\n");
		return -1;
	}
	return 0;
}

int IMP_AO_DisableHpf(void)
{

	IMP_LOG_INFO(TAG, "AO HPF DISABLE\n");
	_reset_aoHpfEnableFlag(0,0);

	fun_ao_hpf_create = NULL;
	fun_ao_hpf_process = NULL;
	fun_ao_hpf_free = NULL;

	return 0;
}

int IMP_AO_SetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	int ret = -1;
	AudioOutDev *pdev = NULL;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}
	pdev = &aoDev[audioDevId];
	if(attr->soundmode != AUDIO_SOUND_MODE_MONO){
		IMP_LOG_ERR(TAG, "ao only support MONO.\n");
		return -1;
	}
	if ((((1000 * attr->numPerFrm)/attr->samplerate)%10) != 0) {
		IMP_LOG_ERR(TAG, "ao  samples(numPerFrm) must be an integer number of 10ms (10ms * n).\n");
		return -1;
	}
	ret = _ao_check_user_attr(pdev, attr);
	if (ret != 0)
		IMP_LOG_ERR(TAG,"line:%d,check user attr failed\n",__LINE__);

	memcpy(&pdev->audioOAttr, attr, sizeof(IMPAudioIOAttr));
	pdev->max_buf_size = sizeof(short)*attr->numPerFrm;

	return 0;
}

int IMP_AO_GetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	AudioOutDev *pdev = NULL;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}
	pdev = &aoDev[audioDevId];
	memcpy(attr, &aoDev[audioDevId].audioOAttr, sizeof(IMPAudioIOAttr));
	attr->numPerFrm = pdev->user_numperfrm;

	return 0;
}

int IMP_AO_Enable(int audioDevId)
{
	int ret = -1;

	IMP_LOG_INFO(TAG, "AO Enable: %d\n", audioDevId);
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}
	if (aoDev[audioDevId].aoEnable == 1) {
		IMP_LOG_ERR(TAG, "Audio Device is already enabled.\n");
		return 0;
	}
	aoDev[audioDevId].aoEnable = 1;
	g_audio_dev_info.ao_en = 1;
	ret = __ao_dev_init(audioDevId);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "__ao_dev_init failed.\n");
		return ret;
	}
	return 0;
}

int IMP_AO_Disable(int audioDevId)
{
	int ret = -1;

	IMP_LOG_INFO(TAG, "AO Disable: %d\n", audioDevId);
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (aoDev[audioDevId].aoEnable == 0) {
		IMP_LOG_ERR(TAG, "ao Device is already Disabled.\n");
		return 0;
	}
	ret = __ao_dev_deinit(audioDevId);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "__ao_dev_deinit failed.\n");
		return ret;
	}

	if(!g_audio_dev_info.ai_en){
		deinit_audioProcess_library(&g_lib_audioProcess_agc_handle);
		deinit_audioProcess_library(&g_lib_audioProcess_hpf_handle);
	}

	g_audio_dev_info.ao_en = 0;
	return 0;

}

int IMP_AO_EnableChn(int audioDevId, int aoChn)
{
	int ret = -1;
	AudioOutDev *pdev = NULL;
	AudioOutChn *pch = NULL;
	IMP_LOG_INFO(TAG, "AO Ch Enable: %d:%d\n", audioDevId, aoChn);
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return 0;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}
	pdev= &aoDev[audioDevId];
	pch = &aoDev[audioDevId].audioOutChn[aoChn];

	pch->ao_tmp_size = pdev->audioOAttr.numPerFrm*sizeof(short);
	pch->ao_tmp_buf = _ao_alloc_buf(pch->ao_tmp_size);
	if (NULL == pch->ao_tmp_buf) {
		IMP_LOG_ERR(TAG,"line:%d,ao play alloc buf failed.\n",__LINE__);
		return -1;
	}

	pch->remainbuf = pch->ao_tmp_buf;
	pch->remainbytes = pch->ao_tmp_size;
	pch->hpf_co_frequency = 0;
	ret = _ao_init_audioProcess_mutex(pch);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"fun:%s\n,init audioProcess mutex failded\n",__func__);
		return -1;
	}
	ret = IMP_AO_IMPDBG_Init();
	if(0 != ret) {
		printf("ao impdbg init fail!!!\n");
	}
	ret = _ao_chn_enable(audioDevId, aoChn);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "_ao_chn_enable failed\n");
		return -1;
	}
	_ao_thread_post();

	IMP_LOG_INFO(TAG, "EXIT AO Ch Enable: %d:%d\n", audioDevId, aoChn);
	return 0;
}

int IMP_AO_DisableChn(int audioDevId, int aoChn)
{
	int ret = -1;

	IMP_LOG_INFO(TAG, "AO Ch Disable: %d:%d\n", audioDevId, aoChn);
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	ret = _ao_chn_disable(audioDevId, aoChn);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "_ao_chn_disable failed\n");
		return -1;
	}
	ret = IMP_AO_IMPDBG_DeInit();
	if(0 != ret) {
		printf("ao impdbg deinit fail!!!\n");
	}

	AudioOutChn *pch = &aoDev[audioDevId].audioOutChn[aoChn];
	ret = _ao_deinit_audioProcess_mutex(pch);
	if (ret != 0){
		IMP_LOG_ERR(TAG,"fun:%s,ao deinit audioProcess mutex failded\n");
		return -1;
	}
	_ao_free_buf(pch->ao_tmp_buf);
	pch->ao_tmp_buf = NULL;

	IMP_LOG_INFO(TAG, "EXIT AO Ch Disable: %d:%d\n", audioDevId, aoChn);

	return 0;
}

void* _ao_get_emptybuf(int audioDevId, int aoChn, IMPBlock block)
{
	int ret = -1;
	AudioOutChn *pch = NULL;
	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	void *aobuf = NULL;

	do {
		ret = pthread_mutex_lock(&mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
		aobuf = audio_buf_get_node(pch->ao_buf, AUDIO_BUF_EMPTY);
		if ((block != BLOCK) || (aobuf != NULL)) {
			ret = pthread_mutex_unlock(&mutex_dev);
			if (0 != ret) {
				printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
				break;
			}
			return aobuf;
		}
		_ao_send_wait();
		ret = pthread_mutex_unlock(&mutex_dev);
		if (0 != ret) {
			printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
			break;
		}
	} while(1);
	return NULL;
}

int IMP_AO_SendFrame(int audioDevId, int aoChn, IMPAudioFrame *data, IMPBlock block)
{
	int ret = -1;
	AudioOutDev *pdev = NULL;
	AudioOutChn *pch = NULL;
	void *aobuf = NULL;
	void *adata = NULL;
	struct aobufinfo *info = NULL;

	if (data == NULL) {
		IMP_LOG_ERR(TAG, "Invalid param data.\n");
		return -1;
	}
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if (!aoDev[audioDevId].audioOutChn[aoChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Ao Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}
	pdev = &aoDev[audioDevId];
	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	if (data->len <= 0) return 0;

	if (data->len > pdev->user_numperfrm*sizeof(short)) {
		  IMP_LOG_ERR(TAG, "ao frame size is too big, max size is %d\n",
		  pdev->user_numperfrm*sizeof(short));
		  return -1;
	}

	/* packet audio data */
	if (data->len < pch->remainbytes) {
		memcpy(pch->remainbuf, data->virAddr, data->len);
		pch->remainbuf += data->len;
		pch->remainbytes -= data->len;
		return 0;
	}
	aobuf = _ao_get_emptybuf(audioDevId, aoChn, block);
	if (aobuf) {

			memcpy(pch->remainbuf, data->virAddr, pch->remainbytes);
			info = audio_buf_node_get_info(aobuf);
			adata = audio_buf_node_get_data(aobuf);
			info->buf = adata;
			info->len = pch->ao_tmp_size;
	                if(ao_frm_num > 0){
		    		dbg_ao_get_frm((uint32_t *)pch->ao_tmp_buf,pch->ao_tmp_size,WRITE_SOURCE);
			}
#if 1
			_ao_hpf_lock(pch);
			if (pch->ao_hpf_enable_suppression) {
				ret = _ao_HPF_Filter(&handle_ao_hpf, (short*)pch->ao_tmp_buf, pch->ao_tmp_size);
				if (ret != 0) {
					IMP_LOG_ERR(TAG,"_ao_Hpf_Filter error.\n");
					return -1;
				}
			}
			_ao_hpf_unlock(pch);

			_ao_agc_lock(pch);
			if (pch->ao_agc_enable_suppression) {
				int outMicLevel = 0;
				unsigned char saturationWarning = 0;

				if (handle_ao_agc != NULL) {
					ret = _ao_Agc_Process(handle_ao_agc, (short*)pch->ao_tmp_buf, agc_sample_o, (short*)pch->ao_tmp_buf,127, &outMicLevel,0, &saturationWarning, pch->ao_tmp_size);
					if (ret != 0) {
						IMP_LOG_ERR(TAG,"IngenicAgc_Process error.\n");
						return ret;
					}
				}
			}
			_ao_agc_unlock(pch);

#endif
			_audio_set_volume((char *)pch->ao_tmp_buf, (char *)info->buf, info->len, 16, pch->vol.vol);

			if (data->len == pch->remainbytes) {
				pch->remainbuf = pch->ao_tmp_buf;
				pch->remainbytes = pch->ao_tmp_size;
			} else {
				pch->remainbuf = pch->ao_tmp_buf;
				memcpy(pch->remainbuf, (char *)data->virAddr+pch->remainbytes, data->len-pch->remainbytes);
				pch->remainbuf += (data->len-pch->remainbytes);
				pch->remainbytes = pch->ao_tmp_size-(data->len-pch->remainbytes);
			}

			ret = pthread_mutex_lock(&mutex_dev);
			if (0 != ret) {
				printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
				return -1;
			}
	                if(ao_frm_num > 0){
		    		dbg_ao_get_frm((uint32_t *)(info->buf),info->len,WRITE_TARGET);
				ao_frm_num --;
				if(ao_frm_num <= 0){
					ao_frm_num  = 0;
				}
			}
			audio_buf_put_node(pch->ao_buf, aobuf, AUDIO_BUF_FULL);
			pch->status.chnFreeNum--;
			pch->status.chnBusyNum++;
			_ao_thread_post();
			ret = pthread_mutex_unlock(&mutex_dev);
			if (0 != ret) {
				printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
				return -1;
			}
			ret = 0;
		}
		return ret;
}

int IMP_AO_PauseChn(int audioDevId, int aoChn)
{
	int ret = -1;
	int retry = 0;
	AudioOutChn *pch = NULL;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	ret = pthread_mutex_lock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pch->pause = 1;
	_ao_thread_post();
	ret = pthread_mutex_unlock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	while ((retry < 30) && (1 != pch->pause_done)) {
		usleep(100*1000);
		retry++;
	}
	if (retry >= 30) {
		printf("warn: wait pause_complete too long time\n");
	}
	pch->pause_done = 0;
	return 0;
}

int IMP_AO_ResumeChn(int audioDevId, int aoChn)
{
	int ret = -1;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	ret = pthread_mutex_lock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	aoDev[audioDevId].audioOutChn[aoChn].pause = 0;
	_ao_thread_post();
	ret = pthread_mutex_unlock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}

	return 0;
}

int IMP_AO_ClearChnBuf(int audioDevId, int aoChn)
{
	int ret = -1;
	AudioOutChn *pch = NULL;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	pch = &aoDev[audioDevId].audioOutChn[aoChn];
	ret = pthread_mutex_lock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	audio_buf_clear(pch->ao_buf);

	pch->status.chnFreeNum = pch->status.chnTotalNum;
	pch->status.chnBusyNum = 0;

	ret = __ao_dev_clearbuf(audioDevId, aoChn);
	if (ret != 0)
		IMP_LOG_ERR(TAG,"ao dev clear buf error!!.\n");

	ret = pthread_mutex_unlock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}

	return 0;
}

int IMP_AO_FlushChnBuf(int audioDevId, int aoChn)
{
	int ret = -1;
	AudioOutChn *pch = NULL;
	void *aobuf = NULL;
	void *adata = NULL;
	struct aobufinfo *info = NULL;

	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	pch = &aoDev[audioDevId].audioOutChn[aoChn];

	if (pch->remainbytes > 0)
		memset(pch->remainbuf, 0x0, pch->remainbytes);

	aobuf = _ao_get_emptybuf(audioDevId, aoChn, BLOCK);
	if (aobuf) {
		info = audio_buf_node_get_info(aobuf);
		adata = audio_buf_node_get_data(aobuf);
		info->buf = adata;
		info->len = pch->ao_tmp_size;
		_audio_set_volume((char *)pch->ao_tmp_buf, (char *)info->buf, info->len, 16, pch->vol.vol);
	}

	ret = pthread_mutex_lock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	pch->sync = 0;
	audio_buf_put_node(pch->ao_buf, aobuf, AUDIO_BUF_FULL);
	pch->status.chnFreeNum--;
	pch->status.chnBusyNum++;
	_ao_thread_post();
	ret = pthread_mutex_unlock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}

	pthread_mutex_lock(&pch->mutex_flush);
	if(pch->status.chnBusyNum)
		_ao_sync_wait(pch);
	pthread_mutex_unlock(&pch->mutex_flush);

	ret = __ao_dev_flushchnbuf(audioDevId, aoChn);
	if (ret != 0) {
		IMP_LOG_ERR(TAG,"ao dev sync chn buf error!!.\n");
		return -1;
	}

	return 0;
}

int IMP_AO_QueryChnStat(int audioDevId, int aoChn, IMPAudioOChnState *status)
{
	int ret = -1;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	ret = pthread_mutex_lock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	memcpy(status, &aoDev[audioDevId].audioOutChn[aoChn].status, sizeof(IMPAudioOChnState));
	ret = pthread_mutex_unlock(&mutex_dev);
	if (0 != ret) {
		printf("err: %s,%d ret= %d\n", __func__, __LINE__, ret);
		return -1;
	}
	return 0;

}

int IMP_AO_Soft_Mute(int audioDevId, int aoChn)
{
	int ret = -1;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	AudioOutChn *pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pthread_t thread_id;
	ret = pthread_create(&thread_id, NULL, _ao_play_mute_thread, (void *)pch);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: pthread_create Ao Play failed\n", __func__);
	}
	pthread_detach(thread_id);

	return 0;
}

int IMP_AO_Soft_UNMute(int audioDevId, int aoChn)
{
	int ret = -1;
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	AudioOutChn *pch = &aoDev[audioDevId].audioOutChn[aoChn];
	pthread_t thread_id;
	ret = pthread_create(&thread_id, NULL, _ao_play_unmute_thread, (void *)pch);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: pthread_create Ao Play failed\n", __func__);
	}
	pthread_detach(thread_id);

	return 0;
}

int IMP_AO_SetVolMute(int audioDevId, int aoChn, int mute)
{
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	if (mute == 1) {
		aoDev[audioDevId].audioOutChn[aoChn].vol.vol = 0.0;
	} else if (mute == 0) {
		aoDev[audioDevId].audioOutChn[aoChn].vol.vol = aoDev[audioDevId].audioOutChn[aoChn].ao_ch_keep_old_vol_coef;
	} else {
		IMP_LOG_ERR(TAG, "Invalid AO Mute Value.\n");
		return -1;
	}

	aoDev[audioDevId].audioOutChn[aoChn].vol.mute = mute;

	return 0;
}

int IMP_AO_SetVol(int audioDevId, int aoChn, int aoVol)
{
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}
	IMPAudioVol vol;
	IMP_LOG_INFO(TAG, "AO Set Vol: %d\n", aoVol);
	vol.minVol = -30;
	vol.maxVol = 120;
	vol.mute   = 0;

	if (aoVol < vol.minVol) aoVol = -30;
	if (aoVol > vol.maxVol) aoVol = 120;

	if (aoVol == -30) {
		vol.vol = 0.0;
	} else {
		/*dB = 20log(Vx / Vo), step is 0.5dB*/
		vol.vol = pow(10, ((((double)aoVol) * 0.5) - 30.0) / 20.0);
	}

	_ao_chn_lock(&aoDev[audioDevId].audioOutChn[aoChn]);
	aoDev[audioDevId].audioOutChn[aoChn].vol.vol = vol.vol;
	aoDev[audioDevId].audioOutChn[aoChn].vol.minVol = vol.minVol;
	aoDev[audioDevId].audioOutChn[aoChn].vol.maxVol = vol.maxVol;
	_ao_chn_unlock(&aoDev[audioDevId].audioOutChn[aoChn]);
	aoDev[audioDevId].audioOutChn[aoChn].ao_ch_keep_old_vol = aoVol;
	aoDev[audioDevId].audioOutChn[aoChn].ao_ch_keep_old_vol_coef = vol.vol;

	return 0;
}

int IMP_AO_GetVol(int audioDevId, int aoChn, int *vol)
{
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	_ao_chn_lock(&aoDev[audioDevId].audioOutChn[aoChn]);
	*vol = aoDev[audioDevId].audioOutChn[aoChn].ao_ch_keep_old_vol;
	_ao_chn_unlock(&aoDev[audioDevId].audioOutChn[aoChn]);
	IMP_LOG_INFO(TAG, "AO Get Vol: %d\n", *vol);
	return 0;
}

int IMP_AO_SetGain(int audioDevId, int aoChn, int aoGain)
{
	int ret = -1;
	if (audioDevId > MAX_AO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AO Get Gain: %d\n", aoGain);
	/*TODO: Just suitable T15 AK4951 Codec,If change Codec,We should modify.*/
	if (aoGain < 0) aoGain = 0;
	if (aoGain > 31) aoGain = 31;
	ret = __ao_dev_set_gain(audioDevId, aoChn, &aoGain);
	if (ret) {
		IMP_LOG_ERR(TAG, "Set ao Play Gain Error.\n");
		ret = -1;
	}
	aoDev[audioDevId].audioOutChn[aoChn].ao_ch_keep_old_gain = aoGain;
	return 0;
}

int IMP_AO_GetGain(int audioDevId, int aoChn, int *aoGain)
{
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}

	*aoGain = aoDev[audioDevId].audioOutChn[aoChn].ao_ch_keep_old_gain;
	IMP_LOG_INFO(TAG, "AO Get Gain: %d\n", *aoGain);

	return 0;
}
#if 0
int IMP_AO_SetDigitalGain(int audioDevId, int aoChn, int dgain)
{
	int ret = -1;
	if (audioDevId > MAX_AO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}
	IMP_LOG_INFO(TAG, "AO Set Digital Gain: %d\n", dgain);
	/*************************************************\
	| vol :   0	     1	     2	   ..	254   255	  |
	| dgain: -120   -119.5   -119  ..	6.5   7 (+DB) |
	\*************************************************/
	//analog gain
	if (dgain < 0)
		dgain = 0;
	if (dgain > 255)
		dgain = 255;
	if ((ret = __ao_dev_set_dgain(audioDevId, aoChn, &dgain)) != 0) {
		IMP_LOG_ERR(TAG, "err: __ai_dev_set_dgain\n");
	}
	aoDev[audioDevId].audioOutChn[aoChn].ao_ch_dgain = dgain;
	return 0;
}

int IMP_AO_GetDigitalGain(int audioDevId, int aoChn,int *dgain)
{
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}
	*dgain = aoDev[audioDevId].audioOutChn[aoChn].ao_ch_dgain;
	IMP_LOG_INFO(TAG, "AO Get Gain: %d\n", *dgain);
	return 0;
}
#endif
int IMP_AO_CacheSwitch(int audioDevId, int aoChn, int cache_en)
{
	AudioOutChn *pch = NULL;
	IMP_LOG_INFO(TAG, "AO Ch Enable: %d:%d\n", audioDevId, aoChn);
	if ((audioDevId > MAX_AO_DEV)||(audioDevId < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
		return -1;
	}

	if (!aoDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Ao device is no enabled.\n", __func__, __LINE__);
		return 0;
	}

	if ((aoChn > MAX_AO_CHN)||(aoChn < 0)) {
		IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
		return -1;
	}
	pch = &aoDev[audioDevId].audioOutChn[aoChn];

	pch->cache_en = cache_en;

	return 0;
}

int IMP_AO_EnableAlgo(int audioDevId, int aoChn)
{

    int ret = -1;
    AudioOutDev *pdev = NULL;
    AudioOutChn *pch = NULL;
    IMPAudioAgcConfig agcConfig;
    int ns_level =0;
    if (audioDevId > MAX_AO_DEV) {
        IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
        return -1;
    }

    if (aoChn > MAX_AO_CHN) {
        IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
        return -1;
    }
    pdev = &aoDev[audioDevId];
    pch = &aoDev[audioDevId].audioOutChn[aoChn];
     //agc
     if(read_profile(pathbuff, "AGC_AO","AGC_enable", profile_buff) < 0){
         IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AO:AGC_enable\n",    __func__);
     }else if(!strcmp(profile_buff, "true")){
         if(read_profile(pathbuff, "AGC_AO","set_mode", profile_buff) < 0){
             IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AO:set_mode,use  default kAdaptiveDigital\n", __func__);
             ao_agc_mode = 2;
         }else if(!strcmp(profile_buff, "kAdaptiveAnalog")){
             ao_agc_mode = 1;
         }else if(!strcmp(profile_buff, "kAdaptiveDigital")){
             ao_agc_mode = 2;
         }else if(!strcmp(profile_buff, "kFixedDigital")){
             ao_agc_mode = 3;
         }else{
             IMP_LOG_ERR(TAG,"%s: select AGC_AO:level (kFixedDigital/kAdaptiveAnalog/kAdaptiveAnalog).use defualt kAdaptiveDigital \n", __func__);
             ao_agc_mode = 2;
         }
         if(read_profile(pathbuff, "AGC_AO","set_target_level_dbfs",profile_buff) < 0){
             IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AO:set_target_level_dbfs, use default 3\n", __func__);
             agcConfig.TargetLevelDbfs = 3;
         }else {
             agcConfig.TargetLevelDbfs = atoi(profile_buff);
         }
         if(read_profile(pathbuff, "AGC_AO","set_compression_gain_db",profile_buff) < 0){
            IMP_LOG_ERR(TAG,"%s: cannot find the value of AGC_AO:set_compression_gain_db, use default 1\n", __func__);
            agcConfig.CompressionGaindB = 1;
        }else{
            agcConfig.CompressionGaindB = atoi(profile_buff);
        }
        ret = IMP_AO_EnableAgc(&pdev->audioOAttr, agcConfig);
        if(ret != 0){
            IMP_LOG_ERR(TAG,"%s: enable agc is fail \n", __func__);
        }

    }else{

        IMP_LOG_ERR(TAG,"%s: AGC_AO  is disable\n", __func__);
    }
     //hpf
     if(read_profile(pathbuff, "HP_AO","HP_enable", profile_buff) < 0){
         IMP_LOG_ERR(TAG,"%s: cannot find the value of HP_AO:HP_enable\n",        __func__);
     }else if(!strcmp(profile_buff, "true")){
         if(read_profile(pathbuff, "HP_AO","cofrequency", profile_buff) < 0){
             IMP_LOG_ERR(TAG,"%s: cannot find the value of HP_AO:cofrequency, use default 400\n", __func__);
             pch->hpf_co_frequency  = 400;
         }else{
             pch->hpf_co_frequency = atoi(profile_buff);
         }
         ret = IMP_AO_EnableHpf(&pdev->audioOAttr);
         if(ret != 0){
             IMP_LOG_ERR(TAG,"%s: enable hpf is fail \n", __func__);
         }
     }else{
         IMP_LOG_ERR(TAG,"%s: HP_AO is disbale\n", __func__);
     }
     return 0;
 }
int IMP_AO_DisableAlgo(int audioDevId, int aoChn)
{
    int ret = -1;
    AudioOutDev *pdev = NULL;
    AudioOutChn *pch = NULL;
    if (audioDevId > MAX_AO_DEV) {
        IMP_LOG_ERR(TAG, "Invalid ao device ID.\n");
        return -1;
    }

    if (aoChn > MAX_AO_CHN) {
        IMP_LOG_ERR(TAG, "Invalid ao channel.\n");
        return -1;
    }
    pch = &aoDev[audioDevId].audioOutChn[aoChn];
    if(pch->ao_agc_enable_suppression == 1)
    {
        IMP_AO_DisableAgc();
    }
    if(pch->ao_hpf_enable_suppression == 1)
    {
        IMP_AO_DisableHpf();
    }
    return 0;
}
